traffic_light_fsm
Source: Lab3Extra/traffic_light_fsm.sv (modified 2025-11-09 23:33)
// traffic_light_fsm.sv
// Moore FSM for two-way traffic lights with mandatory 5-second all-red intervals.
// Author: <Your Name>
// Course: ECE (HW 3.24)
// -----------------------------------------------------------------------------
// Light encoding: 3'b100=RED, 3'b010=YELLOW, 3'b001=GREEN
module TrafficLightFSM #(
// ---- Clock setup ----
parameter int unsigned CLK_HZ = 50_000_000, // board clock; TB will override
parameter int unsigned TICK_HZ = 1, // 1 tick per second by default
// ---- Durations in SECONDS ----
parameter int unsigned T_A_GREEN = 10,
parameter int unsigned T_A_YEL = 3,
parameter int unsigned T_B_GREEN = 10,
parameter int unsigned T_B_YEL = 3,
parameter int unsigned T_ALL_RED = 5
)(
input logic clk,
input logic rst_n, // active-low reset (async assert, sync deassert recommended)
output logic [2:0] LA, // A-side lights {R,Y,G}
output logic [2:0] LB // B-side lights {R,Y,G}
);
// ---------------------------
// 1) clock divider to TICK_HZ
// ---------------------------
localparam longint DIVISOR = CLK_HZ / TICK_HZ;
logic [$clog2(DIVISOR)-1:0] div_cnt;
logic tick_1hz;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
div_cnt <= '0;
tick_1hz <= 1'b0;
end else begin
if (div_cnt == DIVISOR-1) begin
div_cnt <= '0;
tick_1hz <= 1'b1;
end else begin
div_cnt <= div_cnt + 1'b1;
tick_1hz <= 1'b0;
end
end
end
// ---------------------------
// 2) FSM states (Moore)
// ---------------------------
typedef enum logic [2:0] {
S_A_G = 3'd0, // A: Green, B: Red
S_A_Y = 3'd1, // A: Yellow, B: Red
S_ALL_AB = 3'd2, // All-Red between A->B (5s)
S_B_G = 3'd3, // B: Green, A: Red
S_B_Y = 3'd4, // B: Yellow, A: Red
S_ALL_BA = 3'd5 // All-Red between B->A (5s)
} state_t;
state_t state, state_n;
// ---------------------------
// 3) per-state down counter (seconds)
// ---------------------------
int unsigned sec_cnt, sec_cnt_n;
// helper: load value for each state
function automatic int unsigned state_seconds(state_t s);
case (s)
S_A_G : return T_A_GREEN;
S_A_Y : return T_A_YEL;
S_ALL_AB : return T_ALL_RED; // exactly 5 seconds per spec
S_B_G : return T_B_GREEN;
S_B_Y : return T_B_YEL;
S_ALL_BA : return T_ALL_RED; // exactly 5 seconds per spec
default : return T_ALL_RED;
endcase
endfunction
// ---------------------------
// 4) next-state logic
// ---------------------------
always_comb begin
state_n = state;
sec_cnt_n = sec_cnt;
if (tick_1hz) begin
if (sec_cnt == 0) begin
// time to advance
unique case (state)
S_A_G : state_n = S_A_Y;
S_A_Y : state_n = S_ALL_AB; // insert ALL-RED before B gets green
S_ALL_AB : state_n = S_B_G;
S_B_G : state_n = S_B_Y;
S_B_Y : state_n = S_ALL_BA; // insert ALL-RED before A gets green
S_ALL_BA : state_n = S_A_G;
default : state_n = S_A_G;
endcase
// load next state's seconds
sec_cnt_n = state_seconds(state_n) - 1; // we will consume 1s on the next tick edge
end else begin
// still waiting in this state
sec_cnt_n = sec_cnt - 1;
end
end
end
// ---------------------------
// 5) state & counter registers
// ---------------------------
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_ALL_BA; // start from an all-red, then go to A_G
sec_cnt <= state_seconds(S_ALL_BA); // full duration first
end else begin
state <= state_n;
sec_cnt <= sec_cnt_n;
end
end
// ---------------------------
// 6) Moore outputs
// LA/LB are one-hot: {R,Y,G}
// ---------------------------
localparam logic [2:0] RED = 3'b100;
localparam logic [2:0] YELLOW = 3'b010;
localparam logic [2:0] GREEN = 3'b001;
always_comb begin
// defaults
LA = RED;
LB = RED;
unique case (state)
S_A_G : begin LA = GREEN; LB = RED; end
S_A_Y : begin LA = YELLOW; LB = RED; end
S_ALL_AB: begin LA = RED; LB = RED; end
S_B_G : begin LA = RED; LB = GREEN; end
S_B_Y : begin LA = RED; LB = YELLOW; end
S_ALL_BA: begin LA = RED; LB = RED; end
default : begin LA = RED; LB = RED; end
endcase
end
endmodule